﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "PickEventHandler.h"

// gdmp
#include "GDFoundation/ElementSetsUtils.h"
#include "GDFoundation/IElementSets.h"
#include "GDDrawing/DrawingFramePickEventHandler.h"

// gcmp
#include "IDocument.h"
#include "IPickEventArgs.h"
#include "IPick.h"
#include "IPickResult.h"
#include "IUiView.h"
#include "ILinkElementProxy.h"
#include "ILinkDocumentElement.h"
#include "IModelView.h"
#include "IGraphicsNodeReference.h"
#include "GbmpDefines.h"
#include "UiAdapter.h"
#include "ActionModifyInput.h"

using namespace gcmp;

namespace Sample
{
    void RectPickDefaultFilterEventHandler::AddElementSets(const IDocument* pDoc, const IPick* pCandidate, IPick* pPick)
    {
        DBG_WARN_AND_RETURN_VOID_UNLESS(pDoc, L"pDoc为空",L"GDMPLab",L"2024-03-30");
        DBG_WARN_AND_RETURN_VOID_UNLESS(pCandidate, L"pCandidate为空",L"GDMPLab",L"2024-03-30");
        DBG_WARN_AND_RETURN_VOID_UNLESS(pPick, L"pPick为空",L"GDMPLab",L"2024-03-30");

        UnorderedElementIdSet elementSetIds;
        FOR_EACH(pNodeReference, pCandidate->GetAllGraphicsNodeReferences())
        {
            if (pNodeReference == nullptr)
            {
                continue;
            }

            IElement* pElem = pDoc->GetElement(pNodeReference->GetElementId());
            if (pElem == nullptr)
            {
                continue;
            }

            ElementId elemSetId;
            if (const ILinkElementProxy* pLinkEleProxy = quick_cast<ILinkElementProxy>(pElem))
            {
                const ILinkDocumentElement* pLinkDocElement = pLinkEleProxy->GetLinkDocumentElement();
                if (pLinkDocElement)
                {
                    elemSetId = gdmp::ElementSetsUtils::GetDefaultElementSets(pDoc, pLinkDocElement->GetElementId());
                }
            }
            else
            {
                elemSetId = gdmp::ElementSetsUtils::GetDefaultElementSets(pDoc, pElem->GetElementId());
            }
            const gdmp::IElementSets* pElementSets = gdmp::IElementSets::Get(pDoc, elemSetId);
            // element在集合块内;
            if (pElementSets)
            {
                if (std::find(elementSetIds.begin(), elementSetIds.end(), elemSetId) == elementSetIds.end())
                {
                    FOR_EACH(childId, pElementSets->GetChildren())
                    {
                        if (childId == pElem->GetElementId())
                        {
                            continue;
                        }
                        OwnerPtr<IGraphicsNodeReference> opNodeRef = IGraphicsNodeReference::CreateGraphicsElementShapeReference(childId);
                        if (!opNodeRef || opNodeRef.get() == nullptr)
                        {
                            continue;
                        }
                        OwnerPtr<GraphicsNodeAndTransform> opOriginalNodeInfo = pNodeReference->GetIntersectGraphicsNode();
                        if (opOriginalNodeInfo && opOriginalNodeInfo.get())
                        {
                            opNodeRef->SetIntersectData(pNodeReference->GetUntransformedIntersectPoint(), (opOriginalNodeInfo->m_wpGraphicsNode).Get(), (opOriginalNodeInfo->m_opTransfrom).get());
                        }

                        pPick->AddGraphicsNodeReference(*(opNodeRef.get()));
                        elementSetIds.emplace(elemSetId);
                    }
                }
            }
        }
    }

    void RectPickDefaultFilterEventHandler::Process(gcmp::IDocument* pDoc, gcmp::IPickEventArgs* pArgs, const gcmp::Vector3d& ptStart, const gcmp::Vector3d& ptEnd)
    {
        DBG_WARN_AND_RETURN_VOID_UNLESS(pArgs, L"pArgs参数为空",L"GDMPLab",L"2024-03-30");
        DBG_WARN_AND_RETURN_VOID_UNLESS(pDoc, L"pDoc参数为空",L"GDMPLab",L"2024-03-30");
        //在项目文档，轮廓编辑模式环境下，需要保证轮廓线，坡度线优先被拾取;
        if (pDoc->IsFamilyDocument())
            return;

        OwnerPtr<IPickResult> opPickResult = IPickResult::Create();
        DBG_WARN_AND_RETURN_VOID_UNLESS(opPickResult, L"断言`opPickResult`失败",L"GDMPLab",L"2024-03-30");
        OwnerPtr<IPick> opPick = IPick::Create();
        DBG_WARN_AND_RETURN_VOID_UNLESS(opPick, L"断言`opPick`失败",L"GDMPLab",L"2024-03-30");
        OwnerPtr<IPickResult> opResultInput = pArgs->GetPickCandidates().Clone();
        DBG_WARN_AND_RETURN_VOID_UNLESS(opResultInput, L"断言`opResultInput`失败",L"GDMPLab",L"2024-03-30");
        std::vector<OwnerPtr<IPick>>& candidates = opResultInput->GetAllPicks();
        DBG_WARN_AND_RETURN_VOID_UNLESS(candidates.size() == 1, L"rectangle pick的个数应为1",L"GDMPLab",L"2024-03-30");
        DBG_WARN_AND_RETURN_VOID_UNLESS(candidates[0], L"断言`candidates[0]`失败",L"GDMPLab",L"2024-03-30");
        const std::vector<OwnerPtr<IGraphicsNodeReference>>& vGNodeReference = candidates[0]->GetAllGraphicsNodeReferences();
        UnorderedElementIdSet moduleIds;
        for (auto it = vGNodeReference.cbegin(); it != vGNodeReference.cend(); ++it)
        {
            const IGraphicsNodeReference* pGNodeRef = (*it).get();
            DBG_WARN_AND_CONTINUE_UNLESS(pGNodeRef, L"断言`pGNodeRef`失败",L"GDMPLab",L"2024-03-30");
            IElement* pEle = pDoc->GetElement(pGNodeRef->GetElementId());
            if (!pEle)
                continue;
            opPick->AddGraphicsNodeReference(*pGNodeRef);
        }

        bool bAlt = ::GetKeyState(VK_MENU) & 0x8000 ? true : false;
        if (!bAlt)
        {
            AddElementSets(pDoc, candidates[0].get(), opPick.get());
        }

        opPickResult->AddPick(TransferOwnership(opPick));
        pArgs->SetPickCandidates(*opPickResult);
    }


    void PickDefaultFilterEventHandler::AddElementSets(const IDocument* pDoc, std::vector<OwnerPtr<IPick>>& candidates)
    {
        DBG_WARN_AND_RETURN_VOID_UNLESS(pDoc, L"pDoc为空",L"GDMPLab",L"2024-03-30");
        UnorderedElementIdSet elementSetIds;
        std::vector<OwnerPtr<IPick>> newCandidates;
        FOR_EACH(opCandidate, candidates)
        {
            if (!opCandidate)
            {
                continue;
            }

            const gcmp::IGraphicsNodeReference* pNodeReference = opCandidate->GetFront();
            if (pNodeReference == nullptr)
            {
                continue;
            }

            IElement* pElem = pDoc->GetElement(pNodeReference->GetElementId());
            if (pElem == nullptr)
            {
                continue;
            }

            ElementId elemSetId;
            if (const ILinkElementProxy* pLinkEleProxy = quick_cast<ILinkElementProxy>(pElem)) {
                const ILinkDocumentElement* pLinkDocElement = pLinkEleProxy->GetLinkDocumentElement();
                if (pLinkDocElement)
                {
                    elemSetId = gdmp::ElementSetsUtils::GetDefaultElementSets(pDoc, pLinkDocElement->GetElementId());
                }
            }
            else {
                elemSetId = gdmp::ElementSetsUtils::GetDefaultElementSets(pDoc, pElem->GetElementId());
            }

            const gdmp::IElementSets* pElementSets = gdmp::IElementSets::Get(pDoc, elemSetId);
            if (pElementSets == nullptr) {
                // element不在选择集内，直接加入选择结果;
                newCandidates.push_back(TransferOwnership(opCandidate));
                continue;
            }

            if (std::find(elementSetIds.begin(), elementSetIds.end(), elemSetId) == elementSetIds.end())
            {
                FOR_EACH(childId, pElementSets->GetChildren())
                {
                    if (childId == pElem->GetElementId())
                    {
                        continue;
                    }
                    OwnerPtr<IGraphicsNodeReference> opNodeRef = IGraphicsNodeReference::CreateGraphicsElementShapeReference(childId);
                    if (!opNodeRef || opNodeRef.get() == nullptr)
                    {
                        continue;
                    }
                    OwnerPtr<GraphicsNodeAndTransform> opOriginalNodeInfo = pNodeReference->GetIntersectGraphicsNode();
                    if (opOriginalNodeInfo && opOriginalNodeInfo.get())
                    {
                        opNodeRef->SetIntersectData(pNodeReference->GetUntransformedIntersectPoint(), (opOriginalNodeInfo->m_wpGraphicsNode).Get(), (opOriginalNodeInfo->m_opTransfrom).get());
                    }

                    opCandidate->AddGraphicsNodeReference(*(opNodeRef.get()));
                    elementSetIds.emplace(elemSetId);
                }
                newCandidates.push_back(TransferOwnership(opCandidate));
            }
        }
        candidates.swap(newCandidates);
    }

    void PickDefaultFilterEventHandler::Process(gcmp::IDocument* pDoc, gcmp::IPickEventArgs* pArgs, const gcmp::Vector3d& pos)
    {
        DBG_WARN_AND_RETURN_VOID_UNLESS(pArgs, L"pArgs为空",L"GDMPLab",L"2024-03-30");
        DBG_WARN_AND_RETURN_VOID_UNLESS(pDoc, L"pDoc参数为空",L"GDMPLab",L"2024-03-30");
        if (pDoc->IsFamilyDocument())
            return;

        OwnerPtr<IPickResult> opPickResult = pArgs->GetPickCandidates().Clone();
        DBG_WARN_AND_RETURN_VOID_UNLESS(opPickResult, L"断言`opPickResult`失败",L"GDMPLab",L"2024-03-30");
        std::vector<OwnerPtr<IPick>>& candidates = opPickResult->GetAllPicks();
        //拾取选择集时过滤处理
        bool bAlt = ::GetKeyState(VK_MENU) & 0x8000 ? true : false;
        if (!bAlt)
        {
            // FLAG:选择集合
            AddElementSets(pDoc, candidates);
        }

        pArgs->SetPickCandidates(*opPickResult);
    }

    void AddEventHandler(gcmp::ActionModifyInput& input)
    {
        input.AddPickPostProcessEventHandler(NEW_AS_OWNER_PTR(Sample::PickDefaultFilterEventHandler));
        // 将图框拾取后处理器和框选后处理器分别加入到拾取后处理事件和框选后处理事件中。
        // 1、将图框拾取后处理器加入到拾取后处理事件中，当拾取到图框的任何部分时，拾取结果中只会有图框对象。
        // 对应的，在后续的处理预选高亮（gcmp::IPreHighlights）、点选高亮（gcmp::IHighlights）的数据时，
        // 需要将图框识别出来分别调用gdmp::IDrawingframe::PreHighlightInstances()和gdmp::IDrawingframe::HighlightInstances()，以保持图框整体预选高亮和点选高亮。
        // 2、将图框框选后处理器加入到后处理事件中，当框选结束后，图框会被整体加入到框选结果集中。
        input.AddPickPostProcessEventHandler(NEW_AS_OWNER_PTR(gdmp::DrawingFramePickEventHandler));
    }

    void OnTime()
    {
        Sample::UiAdapter::SetActionModifyAddEventHandlerCallBack(AddEventHandler);
    }
    ONE_TIME_INITIALIZER(OnTime)
}
